#include "vz_log.h"
#include "log_file.h"
#include "os_sys.h"
#include "drv_misc.h"

static bool g_bLogInit  = FALSE;
static char g_chLogCache[MAX_RECORD_LOG_LEN] = {0};
static int32_t g_chLogCacheLen = 0;
static int32_t g_i32NewLogCnt = 0;
static rq_lock g_lockVzLog = 0;

int32_t VzLog_Init() {
  if (g_bLogInit == TRUE) {
    log_w("vz_log has been initialized");
    return 0;
  }

  int32_t ret = LogFile_GetHeader();
  if (ret != 0) {
    log_d("open log file failed, only current period log data is valid");
  }
  MUTEX_CREATE(g_lockVzLog);
  g_bLogInit = TRUE;
  return 0;
}

int32_t VzLog_Flush() {
  //vz_log_cache_dump(); //for test
  MUTEX_LOCK(g_lockVzLog);
  LogFile_SyncCache(g_chLogCache, g_chLogCacheLen);
  g_chLogCacheLen = 0;
  MUTEX_UNLOCK(g_lockVzLog);
  return 0;
}

int32_t VzLog_Uninit() {
  g_bLogInit = FALSE;
  MUTEX_DESTROY(g_lockVzLog);
  return 0;
}

void VzLog_Output(int32_t type, int32_t level, int32_t log_id, const char *format, ...) {
  if (!g_bLogInit) {
    return;
  }
  uint64_t time_stamp = 0;;
  int32_t size = 0;
  char data[LOG_MAX_DATA_LEN];
  int32_t limit = LOG_MAX_DATA_LEN - 1;
  MUTEX_LOCK(g_lockVzLog);
  if (format) {
    va_list args;
    va_start(args, format);
    limit = limit - size;
    int32_t ret = vsnprintf(data + size, limit, format, args);
    va_end(args);
    if (ret != -1) {
      size += ret < limit ? ret : limit;
    }
  }
  data[size] = '\0';
  size += 1;
  LogInfo_t single_log_info;
  memset(&single_log_info, 0, sizeof(single_log_info));

  single_log_info.type = type;
  single_log_info.level = level;
  single_log_info.log_id = log_id;
  DrvMisc_GetSec(&time_stamp);
  single_log_info.log_time = time_stamp;
  memcpy(single_log_info.data, data, size < LOG_MAX_DATA_LEN ? size : LOG_MAX_DATA_LEN);
  VzLog_WriteCache((char *) &single_log_info, sizeof(single_log_info));
  g_i32NewLogCnt++;
  MUTEX_UNLOCK(g_lockVzLog);
  return;
}

int32_t VzLog_WriteCache(char *data, int32_t size) {
  if (g_chLogCacheLen + size < MAX_RECORD_LOG_LEN) {
    memcpy(&g_chLogCache[g_chLogCacheLen], data, size);
    g_chLogCacheLen += size;
  } else {
    log_w("log size out of memory, len:%d, limit:%d.",
           g_chLogCacheLen + size, MAX_RECORD_LOG_LEN);
    LogFile_SyncCache(g_chLogCache, g_chLogCacheLen);
    g_chLogCacheLen = 0;
  }

  return 0;
}

int32_t VzLog_GetCache(char *buffer, uint32_t buff_size,
                         uint32_t log_start, uint32_t log_count) {
  uint32_t read_count = 0;
  if ((!buffer) || (buff_size == 0)) {
    log_e("buffer is not valid");
    return -1;
  }
  uint32_t cache_log_cnt = g_chLogCacheLen / sizeof(LogInfo_t);
  if (log_start > cache_log_cnt) {
    log_e("log start is not valid");
    return -1;
  }
  if (log_start + log_count > cache_log_cnt) {
    read_count = cache_log_cnt - log_start;
  } else {
    read_count = log_count;
  }
  if (read_count * sizeof(LogInfo_t) > buff_size) {
    read_count = buff_size / sizeof(LogInfo_t);
  }
  memcpy(buffer, g_chLogCache + (log_start * sizeof(LogInfo_t)),
         read_count * sizeof(LogInfo_t));
  return read_count * sizeof(LogInfo_t);
}

int32_t VzLog_DumpCache() {
  log_v("g_chLogCacheLen:%d......\n", g_chLogCacheLen);
  for (int32_t i = 0; i < g_chLogCacheLen && i < 240; i++) {
    log_v("%x,", g_chLogCache[i]);
  }
  return 0;
}

uint32_t VzLog_GetNewCount() {
  return g_i32NewLogCnt;
}

uint32_t VzLog_GetTotalCount() {
  MUTEX_LOCK(g_lockVzLog);
  uint32_t cache_count = g_chLogCacheLen / sizeof(LogInfo_t);
  uint32_t file_count = LogFile_GetCount();
  MUTEX_UNLOCK(g_lockVzLog);
  return cache_count + file_count;
}

void VzLog_ResetCount() {
  MUTEX_LOCK(g_lockVzLog);
  g_i32NewLogCnt = 0;
  MUTEX_UNLOCK(g_lockVzLog);
}

int32_t VzLog_Read(int type, char *buff, uint32_t buff_size,
                    uint32_t start, uint32_t count) {
  uint32_t read_count = count;
  MUTEX_LOCK(g_lockVzLog);
  uint32_t cache_count = g_chLogCacheLen / sizeof(LogInfo_t);
  uint32_t file_count = LogFile_GetCount();
  uint32_t new_count = g_i32NewLogCnt;
  char *buff_index = buff;
  uint32_t buff_capacity = buff_size;
  uint32_t log_read_size = 0;
  int ret = 0;
  if (type != 0) {
    if (new_count == 0) {
      log_w("not any new log is valid");
      MUTEX_UNLOCK(g_lockVzLog);
      return 0;
    }
    if (start > new_count) {
      log_w("invalid start log index");
      MUTEX_UNLOCK(g_lockVzLog);
      return -1;
    }
    uint32_t real_new_start = file_count + cache_count - new_count;
    if (real_new_start + start < file_count) {
      ret = LogFile_GetData(buff_index, buff_capacity, real_new_start + start,
                              read_count);
      if (ret < 0) {
        MUTEX_UNLOCK(g_lockVzLog);
        return ret;
      }
      log_read_size += ret;
      read_count -= (log_read_size / sizeof(LogInfo_t));
      buff_capacity -= log_read_size;
      buff_index += log_read_size;
      if (read_count > 0) {
        ret = VzLog_GetCache(buff_index, buff_capacity, 0, read_count);
        if (ret < 0) {
          MUTEX_UNLOCK(g_lockVzLog);
          return ret;
        }
        log_read_size += ret;
      }
    } else {
      ret = VzLog_GetCache(buff_index, buff_capacity,
                       real_new_start + start - file_count, read_count);
      if (ret < 0) {
        MUTEX_UNLOCK(g_lockVzLog);
        return ret;
      }
      log_read_size += ret; 
    }
  } else {
    if (start > (file_count + cache_count)) {
      log_w("invalid start log index");
      MUTEX_UNLOCK(g_lockVzLog);
      return -1;
    }
    if (start < file_count) {
      ret = LogFile_GetData(buff_index, buff_capacity, start, read_count);
      if (ret < 0) {
        MUTEX_UNLOCK(g_lockVzLog);
        return ret;
      }
      log_read_size += ret;
      read_count -= (log_read_size / sizeof(LogInfo_t));
      buff_capacity -= log_read_size;
      buff_index += log_read_size;
      if (read_count > 0) {
        ret = VzLog_GetCache(buff_index, buff_capacity, 0, read_count);
        if (ret < 0) {
          MUTEX_UNLOCK(g_lockVzLog);
          return ret;
        }
        log_read_size += ret;
      }
    } else {
      ret = VzLog_GetCache(buff_index, buff_capacity, start - file_count,
                             read_count);
      if (ret < 0) {
        MUTEX_UNLOCK(g_lockVzLog);
        return ret;
      }
      log_read_size += ret;
    }
  }
  MUTEX_UNLOCK(g_lockVzLog);
  return log_read_size;
}

void VzLog_Clear() { 
  MUTEX_LOCK(g_lockVzLog);
  LogFile_Clear();
  g_chLogCacheLen = 0;
  g_i32NewLogCnt = 0;
  MUTEX_UNLOCK(g_lockVzLog);
}
